#pragma once
#include "Common.hpp"
#include "Log.hpp"
#include "InetAddr.hpp"
#include<sys/wait.h>
#include<signal.h>
#include<pthread.h>
#include"ThreadPool.hpp"
// 服务器是禁止拷贝的
using namespace LogModule;
const static int defaultsockfd = -1;
const static int backlog = 8;
using namespace MutexModule;
using namespace ThreadPoolModule;

using task_t =std::function<void()>;//任务类型


class TcpServer : public NoCopy // 直接继承NoCopy类，整个服务器再也不会被拷贝了
{
public:
    TcpServer(uint16_t port)
        : _port(port), _listensockfd(defaultsockfd) // 默认文件描述符设置为-1
        ,_isrunning(false)
    {
    }

    void Init() // 服务器初始化
    {
        //signal(SIGCHLD, SIG_IGN);//忽略子进程结束信号

        // 1、创建套接字文件

        // AF_INET协议家族，IPv4协议，报文类型SOCK_STREAM流式套接字
        _listensockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_listensockfd < 0) // 套接字创建失败了
        {
            LOG(LogLevel::FATAL) << "socket error ";
            exit(SOCKET_ERROR);
        }
        // 这里我们就是创建套接字成功了
        LOG(LogLevel::INFO) << "create socket success" << _listensockfd; // 3

        // 2、bind端口号
        InetAddr local(_port); // 直接传个端口号就行了，绑定任何ip地址

        int n = bind(_listensockfd, local.NetAddrptr(), local.NetAddrlen());
        if (n < 0) // 绑定失败了
        {
            LOG(LogLevel::FATAL) << "bind error " << _listensockfd;
            exit(BIND_ERROR);
        }
        LOG(LogLevel::INFO) << "bind success " << _listensockfd;

        // tcp是面向连接的，别人在通信之前得和你建立连接，所以我们需要监听端口，等待别人连接
        // 3、设置socket状态为 listen
        n = listen(_listensockfd, backlog); // 设置最大连接数为8
        if (n < 0)                    // 监听失败了
        {
            LOG(LogLevel::FATAL) << "listen error " << _listensockfd;
            exit(LISTEN_ERROR);
        }
    }

    class ThreadData
    {
    public:
        ThreadData(int fd,InetAddr&ar,TcpServer*s)
        : sockfd(fd),addr(ar),tsvr(s)
        {}
    public:
        int sockfd;
        InetAddr addr;
        TcpServer*tsvr;//TcpServer 类型的指针引用
    };
    //短服务：
    //长服务：多进程多线程比较适合，你不退，服务也不退
    void Service(int sockfd,InetAddr &peer)
    {
        char  buffer[1024];
        while(true)
        {
            //read的返回值有三种
            //n>0：表示读成功，返回实际读到的字节数
            //n==0：表示对方关闭了连接，返回0
            //n<0：表示读失败，
            //1、先读取数据--读成功了的话，会返回实际读到的字节数
            ssize_t n=read(sockfd, buffer, sizeof(buffer)-1);
            if(n>0)
            {
                buffer[n]=0;//设置为字符串，n<=sizeof(buffer)-1
                LOG(LogLevel::DEBUG)<<peer.StringAddr()<<" say:"<<buffer;
                //2、写回数据
                std::string echo_string="echo#";
                echo_string+=buffer;

                write(sockfd,echo_string.c_str(),echo_string.size());//将消息进行写进去
            }
            else if(n==0)
            {
                LOG(LogLevel::DEBUG)<<peer.StringAddr()<<" 退出了...";
                close(sockfd);//关掉文件描述符
                break;
            }
            else
            {
                LOG(LogLevel::DEBUG)<<peer.StringAddr()<<" 异常...";
                close(sockfd);//关掉文件描述符
                break;
            }
        }
    }

    static void *Routine(void*args)
    {
        pthread_detach(pthread_self());//分离线程，不和父线程共享资源
        //将传入的 void* 类型参数 args 转换回 ThreadData* 类型，
        //以便在线程内部使用客户端连接信息
        ThreadData*td=static_cast<ThreadData*>(args);
        td->tsvr->Service(td->sockfd,td->addr);//调用服务函数，处理客户端请求
        //td->tsvr=this;
        delete td;//将new出来的对象删除
        return nullptr;
    }
    void Run() // 服务器运行
    {
        _isrunning = true;
        while(_isrunning)
        {
            //a、获取链接---从内核中直接获取的，建立连接的过程和accept无关

            //accept获取成功的话会返回一个文件描述符的
            struct sockaddr_in peer;//客户端的地址信息
            socklen_t len=sizeof(sockaddr_in);//结构体的大小
            //如果有连接的话，就返回，如果没有链接的话accept就会出现阻塞
            int sockfd=accept(_listensockfd, CONV(peer),&len);//获取链接
            if(sockfd<0)//获取链接失败了
            {
                LOG(LogLevel::ERROR)<<"accept error"<<sockfd;
                continue;
            }
            InetAddr addr(peer);//网络序列转主机
            //走到这里说明获取链接成功了
            LOG(LogLevel::INFO)<<"accept success,peer addr:"<<addr.StringAddr()<<" sockfd:"<<sockfd;//输出主机地址
        
            //单进程程序，只能处理一个客户端
            //Service(sockfd,addr);//处理客户端请求,服务的客户也传上去

            //多进程版本
            // pid_t id=fork();//父进程调用fork
            // if(id<0)
            // {
            //     LOG(LogLevel::ERROR)<<"fork error";
            //     exit(FORK_ERROR);
            // }
            // else if(id==0)
            // {
            //     //子进程，子进程处理看到sockfd，还能看到父进程的listensockfd
            //     //我们不想让子进程访问listensockfd，所以这里关闭listensockfd
            //     close(_listensockfd);

            //     if(fork()>0)//再次fork， 子进程退出
            //         exit(OK);
            //     Service(sockfd,addr);//孙子进程处理客户端请求，
            //     //成了一个孤儿进程了，系统会回收这个孙子进程的
            //     exit(OK);//处理完立刻退出
                
            // }
            // else
            // {
            //     //父进程
            //     close(sockfd);//关闭客户端的套接字

            //     //父进程要等待子进程结束，要不然僵尸了
            //     pid_t rid=waitpid(id,nullptr,0);//
            //     //这个时候就不会阻塞了，因为子进程已经退出了，
            //     (void)rid;
            // }

//             //多线程版本
//             ThreadData*td=new ThreadData(sockfd,addr,this);//创建一个类对象 
// //在 TcpServer 类的成员函数里，this 指向当前 TcpServer 对象自己
//             //因为我们是类内对象，有一个this指针

//             pthread_t tid;//线程id
//             //td 是传递给线程函数 Routine 的参数
//             pthread_create(&tid,nullptr,Routine,td);//创建线程

            //线程池版本：线程池一般比较适合处理短服务
            //将新链接和客户端构建一个新的任务，push到线程池中

            //任务类型的对象进行入队列
            //将当前对象和sockfd、addr传进来
            ThreadPool<task_t>::GetInstance()->Enqueue([this,sockfd,&addr](){
                this->Service(sockfd,addr);//处理客户端请求
            });
        }
        _isrunning = false;//运行完成了就置为false


    }
    ~TcpServer()
    {
    }

private:
    uint16_t _port; // 端口号

    int _listensockfd; // 监听套接字

    bool _isrunning; // 服务器是否运行

};